import ast
import functools
import imp
import importlib
import inspect
import itertools
import logging
import os
import pkg_resources
import re
import sys
import time
import types
import unittest
import threading
from operator import itemgetter
from os.path import join as opj

import odoo
import odoo.tools as tools
import odoo.release as release
from odoo import SUPERUSER_ID, api
from odoo.tools import pycompat

MANIFEST_NAMES = ('__manifest__.py', '__openerp__.py')
README = ['README.rst', 'README.md', 'README.txt']

_logger = logging.getLogger(__name__)

# addons path as a list
ad_paths = []
hooked = False

# Modules already loaded
loaded = []

class AddonsHook(object):

    def find_module(self, name, path=None):
        if name.startswith(('odoo.addons.', 'openerp.addons.')) \
                and name.count('.') == 2:
            return self

    def load_module(self, name):
        assert name not in sys.modules

        odoo_name = re.sub(r'^openerp.addons.(\w+)$', r'odoo.addons.\g<1>', name)
        openerp_name = re.sub(r'^odoo.addons.(\w+)$', r'openerp.addons.\g<1>', odoo_name)

        assert odoo_name not in sys.modules
        assert openerp_name not in sys.modules

        _1, _2, addon_name = name.split('.')
        f, path, (_suffix, _mode, type_) = imp.find_module(addon_name, ad_paths)
        if f: f.close()

        new_mod = types.ModuleType(odoo_name)
        new_mod.__loader__ = self

        assert type_ == imp.PKG_DIRECTORY, "Odoo addon top-level must be a package"
        modfile = opj(path, '__init__.py')
        new_mod.__file__ = modfile
        new_mod.__path__ = [path]
        new_mod.__package__ = odoo_name

        sys.modules[odoo_name] = sys.modules[openerp_name] = new_mod

        exec(open(modfile, 'rb').read(), new_mod.__dict__)

        setattr(odoo.addons, addon_name, new_mod)

        return sys.modules[name]

pkg_resources.register_loader_type(AddonsHook, pkg_resources.DefaultProvider)

class OdooHook(object):

    def find_module(self, name, path=None):

        if re.match(r'^openerp\b', name):
            return self

    def load_module(self, name):
        pass


def initialize_sys_path():

    global ad_paths
    global hooked

    dd = os.path.normcase(tools.config.addons_data_dir)
    if os.access(dd, os.R_OK) and dd not in ad_paths:
        ad_paths.append(dd)

    for ad in tools.config['addons_path'].split(','):
        ad = os.path.normcase(os.path.abspath(tools.ustr(ad.strip())))
        if ad not in ad_paths:
            ad_paths.append(ad)

    base_path = os.path.normcase(os.path.abspath(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'addons')))
    if base_path not in ad_paths and os.path.isdir(base_path):
        ad_paths.append(base_path)


    for ad in __import__('odoo.addons').addons.__path__:
        ad = os.path.abspath(ad)
        if ad not in ad_paths and os.path.isdir(ad):
            ad_paths.append(ad)

    if not hooked:
        sys.meta_path.insert(0, OdooHook())
        sys.meta_path.insert(0, AddonsHook())
        hooked = True

def get_module_path(module, downloaded=False, display_warning=True):
    initialize_sys_path()
    for adp in ad_paths:
        files = [opj(adp, module, manifest) for manifest in MANIFEST_NAMES] + \
                [opj(adp, module + '.zip')]
        if any(os.path.exists(f) for f in files):
            return opj(adp, module)

    if downloaded:
        return opj(tools.config.addons_data_dir, module)
    if display_warning:
        _logger.warning('module %s: module not found', module)
    return False

def get_module_filetree(module, dir='.'):
    pass

def get_resource_path(module, *args):
    mod_path = get_module_path(module)
    if not mod_path: return False
    resource_path = opj(mod_path, *args)
    if os.path.isdir(mod_path):
        # the module is a directory - ignore zip behavior
        if os.path.exists(resource_path):
            return resource_path
    return resource_path

get_module_resource = get_resource_path

def get_resource_from_path(path):
    pass

def get_module_icon(module):
    iconpath = ['static', 'description', 'icon.png']
    if get_module_resource(module, *iconpath):
        return ('/' + module + '/') + '/'.join(iconpath)
    return '/base/' + '/'.join(iconpath)

def module_manifest(path):
    if not path:
        return None
    for manifest_name in MANIFEST_NAMES:
        if os.path.isfile(opj(path, manifest_name)):
            return opj(path, manifest_name)

def get_module_root(path):
    pass

def load_information_from_description_file(module, mod_path=None):

    if not mod_path:
        mod_path = get_module_path(module, downloaded=True)
    manifest_file = module_manifest(mod_path)
    if manifest_file:
        # default values for descriptor
        info = {
            'application': False,
            'author': 'Odoo S.A.',
            'auto_install': False,
            'category': 'Uncategorized',
            'depends': [],
            'description': '',
            'icon': get_module_icon(module),
            'installable': True,
            'license': 'LGPL-3',
            'post_load': None,
            'version': '1.0',
            'web': False,
            'sequence': 100,
            'summary': '',
            'website': '',
        }
        info.update(pycompat.izip(
            'depends data demo test init_xml update_xml demo_xml'.split(),
            iter(list, None)))
        f = tools.file_open(manifest_file, mode='rb')
        try:
            info.update(ast.literal_eval(pycompat.to_native(f.read())))
        finally:
            f.close()

        if not info.get('description'):
            readme_path = [opj(mod_path, x) for x in README
                           if os.path.isfile(opj(mod_path, x))]
            if readme_path:
                readme_text = tools.file_open(readme_path[0]).read()
                info['description'] = readme_text

        if 'active' in info:
            info['auto_install'] = info['active']

        info['version'] = adapt_version(info['version'])
        return info

    _logger.debug('module %s: no manifest file found %s', module, MANIFEST_NAMES)
    return {}

def load_openerp_module(module_name):
    global loaded
    if module_name in loaded:
        return

    initialize_sys_path()
    try:
        __import__('odoo.addons.' + module_name)

        info = load_information_from_description_file(module_name)
        if info['post_load']:
            getattr(sys.modules['odoo.addons.' + module_name], info['post_load'])()

    except Exception as e:
        msg = "Couldn't load module %s" % (module_name)
        _logger.critical(msg)
        _logger.critical(e)
        raise
    else:
        loaded.append(module_name)


def get_modules():
    pass

def get_modules_with_version():
    pass

def adapt_version(version):
    serie = release.major_version
    if version == serie or not version.startswith(serie + '.'):
        version = '%s.%s' % (serie, version)
    return version

def get_test_modules(module):
    pass

def _get_tests_modules(path, module):
    pass

class TestStream(object):
    def __init__(self, logger_name='odoo.tests'):
        pass
    def flush(self):
        pass
    def write(self, s):
        pass


current_test = None

def run_unit_tests(module_name, dbname, position='at_install'):
    pass

def unwrap_suite(test):
    pass


